package cn.zwx.mvcframework.context;

import cn.zwx.mvcframework.annotation.ZwxAutowired;
import cn.zwx.mvcframework.annotation.ZwxController;
import cn.zwx.mvcframework.annotation.ZwxService;
import cn.zwx.mvcframework.aop.ZwxCgliDynamicAopProxy;
import cn.zwx.mvcframework.aop.ZwxJdkDynamicAopProxy;
import cn.zwx.mvcframework.aop.config.ZwxAopConfig;
import cn.zwx.mvcframework.aop.support.ZwxAdvisedSupport;
import cn.zwx.mvcframework.beans.ZwxBeanWrapper;
import cn.zwx.mvcframework.beans.config.ZwxBeanDefinition;
import cn.zwx.mvcframework.beans.support.ZwxBeanDefinitionReader;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 解析配置文件
 * 把扫描到Bean转换为BeanDefinition 并且保存
 * 提供getBean各核心方法
 * @author zhangwenxue 
 * @date 2022/4/24 22:19
*/ 
public class ZwxApplicationContext {

    private Map<String,ZwxBeanDefinition> beanDefinitionMap = new TreeMap<>();

    private final Map<String,Object> factoryBeanObjectCache = new HashMap<>();

    private final Map<String,ZwxBeanWrapper> factoryBeanInstanceCache = new HashMap<>();
    /**
     * 读取配置文件
     * 扫描相关类
     * 保存为一个集合
     */
    ZwxBeanDefinitionReader reader;

    /**
     * 初始化容器 解析配置文件
     * @param configLocations 请求参数
     */
    public ZwxApplicationContext(String... configLocations) {
        // 1.加载配置文件
        reader = new ZwxBeanDefinitionReader(configLocations);
        // 2.把扫描到类转为BeanDefinition并保存起来
        List<ZwxBeanDefinition> beanDefinitionList = reader.loadBeanDefinitions();
        // 3.把读取到BeanDefinition缓存起来
        this.doRegisterBeanDefinition(beanDefinitionList);
        // 4.进行DI操作
        this.doAutowired();

    }

    private void doAutowired() {
        // springIOC中的类 默认是懒加载 只有在调用getBean的时候才实例化
        // 而依赖注入前提就是类示例化才能进行DI操作 
        for (Map.Entry<String, ZwxBeanDefinition> entry : beanDefinitionMap.entrySet()) {
            this.getBean(entry.getKey());
        }
        
    }

    /**
     * Bean的实例化，DI是从而这个方法开始的
     */
    public Object getBean(String className) {
        // 拿到beanDefinitionMap对象
        ZwxBeanDefinition beanDefinition = beanDefinitionMap.get(className);
        // 反射进行实例化
        Object instantiateBean = this.instantiateBean(beanDefinition);
        // 转换BeanWrapper
        ZwxBeanWrapper beanWrapper = this.doCreateBeanWrapper(instantiateBean);
        // 保存到IOC容器中
        factoryBeanInstanceCache.put(className,beanWrapper);
        // 执行依赖注入
        this.populateBean(beanWrapper);

        return instantiateBean;

    }

    private ZwxBeanWrapper doCreateBeanWrapper(Object instantiateBean) {
        ZwxBeanWrapper beanWrapper = new ZwxBeanWrapper();
        beanWrapper.setWrapperInstance(instantiateBean);
        beanWrapper.setWrappedClass(instantiateBean.getClass());
        return beanWrapper;
    }

    private void populateBean(ZwxBeanWrapper beanWrapper) {
        //可能涉及到循环依赖？
        //A{ B b}
        //B{ A b}
        //用两个缓存，循环两次
        //1、在加载A类时 SpringBeanWrapper有个标识 如果存在循环依赖 则A先初始化一部分就先暴露出来 存在一个集合里面
        //2、然后在加载B是 注入A类 B类初始化完毕 保存到另外一个集合 然后A在容器里面拿到这个对象注入完成初始化操作
        Class<?> wrappedClass = beanWrapper.getWrappedClass();
        Object wrapperInstance = beanWrapper.getWrapperInstance();
        // 在Spring里面是@Component Controller Service 等等注解本质上是Component
        if (!(wrappedClass.isAnnotationPresent(ZwxController.class) || wrappedClass.isAnnotationPresent(ZwxService.class))){
            return;
        }
        Field[] declaredFields = wrappedClass.getDeclaredFields();
        if (declaredFields.length == 0){
            return;
        }
        for (Field declaredField : declaredFields) {
            // 没有贴注入注解的 直接忽略
            if (!declaredField.isAnnotationPresent(ZwxAutowired.class)){continue;}
            ZwxAutowired fieldAnnotation = declaredField.getAnnotation(ZwxAutowired.class);
            String beanName = fieldAnnotation.value().trim();
            if ("".equals(beanName)) {
                beanName = declaredField.getType().getName();
            }
            declaredField.setAccessible(true);
            try {
                Object instance = null;
                if (!factoryBeanObjectCache.containsKey(beanName)){
                    // 这里需要处理下 比如controller先过来 依赖注入的service还没有实例化好 导致无法注入问题
                    Class<?> aClass = Class.forName(beanName);
                    if (aClass.isInterface()){
                        // 这里暂时使用SPI机制去加载接口类的所有实现类 正经路子是 使用获取当前ClassLoader 然后在里面去筛选
                        ServiceLoader<?> serviceLoader = ServiceLoader.load(aClass);
                        for (Object obj : serviceLoader) {
                            Class<?> subClass = obj.getClass();
                            String name = subClass.getSimpleName();
                            // 如果是指定类型
                            if (name.equals(beanName)){
                                instance =  this.initAopProxy(subClass.newInstance());
                                factoryBeanInstanceCache.put(subClass.getName(),doCreateBeanWrapper(instance));
                                factoryBeanInstanceCache.put(subClass.getSimpleName(),doCreateBeanWrapper(instance));
                            }
                        }
                        // 如果没有匹配上默认取第一个
                        if (!factoryBeanInstanceCache.containsKey(beanName)){
                            instance =  this.initAopProxy(serviceLoader.iterator().next());
                            factoryBeanInstanceCache.put(instance.getClass().getSimpleName(),doCreateBeanWrapper(instance));
                            factoryBeanInstanceCache.put(instance.getClass().getName(),doCreateBeanWrapper(instance));
                        }

                    }
                }else {
                    instance = factoryBeanObjectCache.get(beanName);
                }
                declaredField.set(wrapperInstance,instance);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    private  Object instantiateBean(ZwxBeanDefinition beanDefinition) {
        String beanClassName = beanDefinition.getBeanClassName();
        Object cacheInstance = factoryBeanObjectCache.get(beanClassName);
        if (Objects.isNull(cacheInstance)){
            try {
                Class<?> clazz = Class.forName(beanClassName);
                Object newInstance = clazz.newInstance();
                // 搞一个本地缓存
                factoryBeanObjectCache.put(beanDefinition.getBeanClassName(),newInstance);

                return this.initAopProxy(newInstance);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
       return cacheInstance;

    }

    /**
     * //==================AOP开始=========================
     */
    private Object initAopProxy(Object newInstance) {
        //如果满足条件，就直接返回Proxy对象
        //1、加载AOP的配置文件
        Class<?> instanceClass = newInstance.getClass();
        ZwxAdvisedSupport config = this.instanceAopConfig();
        config.setTarget(newInstance);
        config.setTargetClass(instanceClass);
        //判断规则，要不要生成代理类，如果要就覆盖原生对象
        //如果不要就不做任何处理，返回原生对象
        if(config.pointCutMath()){
          return new ZwxJdkDynamicAopProxy(config).getProxy();
//            if (instanceClass.isInterface()){
//                newInstance = new ZwxJdkDynamicAopProxy(config).getProxy();
//            }else{
//                newInstance = new ZwxCgliDynamicAopProxy(config).getProxy();
//            }
        }
        return newInstance;
    }

    private ZwxAdvisedSupport instanceAopConfig() {
        ZwxAopConfig config = new ZwxAopConfig();
        config.setPointCut(this.reader.getContextConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getContextConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getContextConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getContextConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getContextConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getContextConfig().getProperty("aspectAfterThrowingName"));
        return new ZwxAdvisedSupport(config);
    }

    public Object getBean(Class clazz){
        return this.getBean(clazz.getName());
    }

    private void doRegisterBeanDefinition(List<ZwxBeanDefinition> beanDefinitionList) {
        if (beanDefinitionList.isEmpty()){
            return;
        }
        for (ZwxBeanDefinition zwxBeanDefinition : beanDefinitionList) {
            if (!beanDefinitionList.contains(zwxBeanDefinition)){
                throw new RuntimeException("The " + zwxBeanDefinition.getFactoryBeanName() + "is exists");
            }
            beanDefinitionMap.put(zwxBeanDefinition.getBeanClassName(),zwxBeanDefinition);
            beanDefinitionMap.put(zwxBeanDefinition.getFactoryBeanName(),zwxBeanDefinition);
            // 这里需要排个顺序 让service先加载
        }
    }


    public int getBeanDefinitionCount() {
        return beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionBeanNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[]{});
    }

    public Properties getConfig() {
        return  reader.getContextConfig();
    }
}
